/** ****************************************************************************
 * CopyRight (C) 2012 Sharadendu Sinha.
 * Licensing : FreeBSD 
 *
 * @(#) $Id$
 */
package com.sps.signalling;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.sps.signalling.Slot.SlotHandler;
import com.sps.signalling.exceptions.SignallingException;

/**
 * This class provides a reflection based implementation of a
 * {@link SlotHandler} This class offers some level of freedom for argument list
 * {@link Signal} emmision and {@link Slot} invocation. For example a signal is
 * emmitted with parameters emit(int, int, List) the following are permissible
 * Slot method argument list. <li>slotMethod()</li> <li>slotMethod(int)</li> <li>
 * slotMethod(int, int)</li> <li>slotMethod(int, int, List)</li> <li>
 * slotMethod(int, int, list, ....)</li> <br>
 * The general algorithm while {@link Slot} method is invoked is as follows<br>
 * <li>if signal arguments.length == slot method arguments.length invoke it as
 * is</li> <li>if signal arguments.length < slot method arguments.length ,
 * invoke the slot with missing values as default. (if primitive then 0 else
 * null)</li> <li>if signal arguments.length > slot method arguments.length ,
 * invoke the slot with appropriate number of arguments and skipping extra
 * parameters in signal emmision</li><br>
 * However if argument ordering in signal and slot mismatch then a
 * {@link SignallingException} will be thrown.
 * 
 * @version 1.1
 * @author sharadendu
 */
public class ReflectiveSlotHandler
	implements SlotHandler
{
	private final Method	slotMethod;
	private final Object	instance;
	@SuppressWarnings("rawtypes")
	private final Class[]	parameterTypes;

	/**
	 * Constructor for ReflectiveSlotHandler
	 *  
	 * @param methodname {@link Method} name
	 * @param parameterTypes {@link Method} parameter types
	 * @param instance the object on which method is to be invoked
	 * @throws SignallingException if {@link Method} is not found in instance
	 *             parameter
	 ****************************************************************************/
	@SuppressWarnings("rawtypes")
	public ReflectiveSlotHandler(String methodname, Class[] parameterTypes,
		Object instance) throws SignallingException
	{
		this.instance = instance;
		this.parameterTypes = parameterTypes;
		try
		{
			this.slotMethod = instance.getClass().getDeclaredMethod(methodname,
				parameterTypes);
		}
		catch(SecurityException e)
		{
			throw new SignallingException(
				SignallingException.INVALID_METHOD_DESCRIPTION, e);
		}
		catch(NoSuchMethodException e)
		{
			throw new SignallingException(
				SignallingException.INVALID_METHOD_DESCRIPTION, e);
		}
	}
	/**
	 * @see com.sps.signalling.Slot.SlotHandler#handleSlotInvcocation(com.sps.signalling.Slot,
	 *      java.lang.Object[]). Invokes {@link Method} using
	 *      {@link Method#invoke(Object, Object...)}. See the class description
	 *      for details of method invocation rules.
	 */
	@Override
	public void handleSlotInvcocation(Slot slot, Object... objects)
		throws SignallingException
	{
		Object[] params = null;
		if(objects.length == parameterTypes.length)
		{
			params = objects;
		}
		else if(objects.length < parameterTypes.length)
		{
			params = new Object[parameterTypes.length];
			System.arraycopy(objects, 0, params, 0, objects.length);
			for(int i = objects.length; i < parameterTypes.length; i++)
			{
				if(parameterTypes[i].isPrimitive())
				{
					params[i] = 0;
				}
			}
		}
		else if(objects.length > parameterTypes.length)
		{
			params = new Object[parameterTypes.length];
			System.arraycopy(objects, 0, params, 0, params.length);
		}
		try
		{
			slotMethod.invoke(instance, params);
		}
		catch(IllegalArgumentException e)
		{
			throw new SignallingException(
				SignallingException.INVOCATION_EXCEPTION, e);
		}
		catch(IllegalAccessException e)
		{
			throw new SignallingException(
				SignallingException.INVOCATION_EXCEPTION, e);
		}
		catch(InvocationTargetException e)
		{
			throw new SignallingException(
				SignallingException.INVOCATION_EXCEPTION, e);
		}
	}
}
